Generally, computer applications run by executing object code. The object code controls the actions of the computer systems on which it is run. Such code may be made public or otherwise made accessible by its authors, for example by publishing the original source code that was compiled to create the object code. The original authors may also choose to make the code more usable by other programmers by including “debug symbols” which are data files which help to describe the structure of the object code so that users of the code can debug their own programs. However, for some uses, it is advisable to protect code from examination by possible adversaries. For example, where the code represents the best available implementation of a particular algorithm, the code itself may represent a trade secret. In another example, where code is used to secure content, it may be useful to protect the code in order to ensure the security of the content from an adversary. Maintaining the security of content may be useful in many contexts. One such context is where the content is private information, for example, financial information about a computer user, user passwords, or other personal information. Another context in which maintaining the security of content is useful is when the secure content is content which is to be used only in a limited way. For example, copyrighted content may be protected by requiring a license for use.
License provisions may be enforced for secure content by a digital rights management (DRM) application. In some DRM applications, content is stored only in an encrypted form, and then decrypted for use according to an electronic license which defines the specific user's rights to use the content. Some or all of the code and associated decryption data which decrypts the encrypted content is therefore protected, because if this code which accesses the copyrighted context is compromised, it may be possible to access the copyright outside of the bounds of the electronic license, for unlicensed uses.
Code which protects sensitive information or performs other sensitive operations is, in some contexts, referred to as a “black box.” The black box may include hardware elements along with software elements. When a black box (or other sensitive code) is being used on a computer system, an adversary may use several means in order to attempt to compromise the security of the operations of the black box. For example, an adversary may attempt to trace the code running in the black box. One way in which an adversary may attempt to do this is by using a debugger to track the progress of the code. Another way an adversary may attempt to compromise the security of the black box is by making modifications to the code in order to provide access to the secure content to the adversary.
In order to prevent such attacks, adversarial detection measures may be used. These measures detect adversarial attacks and cause the black box (or other sensitive code) to cease functioning, so as to prevent the attack from continuing and discovering sensitive information. One such detection measure is caller verification. In a complex software application, one portion of code may call on the functionality of other portions of code. When a secure function or other piece of sensitive code is called by some outside caller, it receives caller identity data concerning the identity of the caller. The secure function will only execute if the caller has the correct security or trustedness. However, the caller identity data which identifies the supposed caller to the secure function may be manipulated. An adversary may take advantage of this in order to hide a call to the secure function by an unauthorized caller. A technique known as caller verification can be used to ensure that the caller identity data does, in fact, identify the correct caller. If a situation is detected in which caller verification has failed, the code halts.
Other measures identify that code has been tampered with or that a debugger is being used. For example, a check may be performed to detect that some portion of memory storing code or data associated with code has been modified. A breakpoint check may be performed to determine if breakpoints have been inserted into the code. Such breakpoints are used to allow an adversary to debug the sensitive code. When measures such as these are used, upon detection of an attack, execution of the sensitive code halts.
However, these measures provide the attacking adversary with some information which may be useful in future attacks. That is, when an adversary detects that a mechanism of attack has caused a failure, the adversary gains a foothold in planning a new attack—the adversary can determine what caused the failure and can attempt to avoid causing such a failure with the adversary's next attack.
For example, the code may include statements corresponding to the following pseudocode:
Perform adversary_detect_measureIf adversary_detect_measure = true then fail
If this code is found, it may be clear that this is an adversary detection measure. The pattern of the code may provide this information even if the attack detection measure is named “check301” rather than “adversary_detect_measure.” It will then be obvious to an adversary that the check must be circumvented in order to prevent the code from halting. If the adversary discovers this, or if the adversary notes that this is where the code halts when the adversary has launched a certain kind of attack, the adversary may add bypassing code. Among many possible bypassing solutions, this may, for example, leave the code in the state corresponding to the following pseudocode:
Jump to BypassPerform adversary_detect_measureIf adversary_detect_measure = true then failBypassIn this way, the detection measure may be circumvented.
In view of the foregoing, there is a need for a system that overcomes the drawbacks of the prior art.